package com.yonyou.pmclouds.plan.controller;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import com.alibaba.dubbo.config.annotation.Reference;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.consts.BillTypeConst;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.exception.IllegalRequestArgException;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.CommonFieldConst;
import com.yonyou.pmclouds.billtype.entity.BillTypeRefVO;
import com.yonyou.pmclouds.milestone.entity.MilestoneVO;
import com.yonyou.pmclouds.milestone.service.IMilestoneQuery;
import com.yonyou.pmclouds.outter.nc.NcBillTypeCustomerQuery;
import com.yonyou.pmclouds.outter.nc.NcDeptCustomerQuery;
import com.yonyou.pmclouds.outter.user.UserInfoQuery;
import com.yonyou.pmclouds.plan.entity.PlanTempletVO;
import com.yonyou.pmclouds.plan.entity.PlanTempletViewVO;
import com.yonyou.pmclouds.plan.plandriven.entity.NcDepartmentVO;
import com.yonyou.pmclouds.plan.plandriven.entity.NcDeptQueryParamVO;
import com.yonyou.pmclouds.plan.rmiitf.PlanTempletService;
import com.yonyou.pmclouds.user.entity.UserInfo;
import com.yonyou.pmclouds.user.entity.UserSearchResultVO;
import net.sf.mpxj.*;
import net.sf.mpxj.mpp.MPPReader;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

@RestController
@RequestMapping("/planTemplet")
public class PlanTempletController {
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private PlanTempletService planTempletService;
    @Autowired
    private UserInfoQuery userInfoQuery;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private IMilestoneQuery milestoneQuery;

    @Autowired
    private NcBillTypeCustomerQuery ncBillTypeCustomerQuery;
    @Autowired
    private NcDeptCustomerQuery ncDeptCustomerQuery;

    private static final String[] USER_ID_FIELDS = {PlanTempletVO.CREATOR, PlanTempletVO.MANAGER};
    private static final String[] USER_NAME_FIELDS = {PlanTempletVO.CREATOR_NAME, PlanTempletVO.MANAGER_NAME};

    @GetMapping("/search")
    public PlanTempletViewVO[] search(@RequestParam("keyword") String keyword) throws BusinessException {
        PlanTempletVO[] templetVOS = planTempletService.searchPlanTemp(keyword, RuntimeEnvironment.getTenantId());
        if(templetVOS == null || templetVOS.length == 0) return null;
        //设置前置计划名称
        setDependPlanName(templetVOS);

        transAndFillViewVO(templetVOS);

        userInfoQuery.tranUserInfoField(templetVOS, USER_ID_FIELDS, USER_NAME_FIELDS);
        Map<String, List<PlanTempletVO>> childMap = genChildMap(templetVOS);
        List<PlanTempletVO> result = childMap.get(StringUtils.EMPTY);

        Map<String, List<PlanTempletVO>> typeTempMap = new LinkedHashMap<>();
        // 计划类型主键和名称的映射
        Map<String, String> typeNameMap = new HashMap<>();
        for(PlanTempletVO vo : result){
            List<PlanTempletVO> vos = typeTempMap.get(vo.getType());
            if(vos == null){
                vos = new ArrayList<>();
                typeTempMap.put(vo.getType(), vos);
            }
            vos.add(vo);
            typeNameMap.put(vo.getType(), vo.getTypeName());
        }

        PlanTempletViewVO[] viewVOS = new PlanTempletViewVO[0];
        for(String key : typeTempMap.keySet()){
            PlanTempletViewVO viewVO = new PlanTempletViewVO();
            viewVO.setType(key);
            viewVO.setTypeName(typeNameMap.get(key) == null ? key : typeNameMap.get(key));
            viewVO.setTempletVOS(typeTempMap.get(key));
            viewVOS = ArrayUtils.add(viewVOS, viewVO);
        }
        return viewVOS;
    }

    /**
     * 获取负责人集合（当前租户下的用户）
     *
     * @param keyword
     * @return
     * @throws BusinessException
     */
    @GetMapping("/searchUser")
    @ResponseBody
    public UserInfo[] searchUser(@RequestParam(value = "keyword", required = false) String keyword) throws BusinessException {
        UserSearchResultVO.ContentVO users = userInfoQuery.searchUsers(RuntimeEnvironment.getTenantId(), keyword,
                1, 1000);
        if (users == null)
            return new UserInfo[0];

        return users.getContent();
    }

    @PostMapping("/saveTemp")
    public void saveTemp(@RequestBody PlanTempletVO temp) throws BusinessException {
        if(temp == null) throw new BusinessException("模板不能为空！");
        if(StringUtils.isBlank(temp.getSname())) throw new BusinessException("模板名称不能为空");
        planTempletService.savePlanTemp(temp);
    }

    @GetMapping("/queryByRoot")
    public PlanTempletVO[] queryByRoot(@RequestParam("rootId") String rootId) throws BusinessException {
        PlanTempletVO[] allVOs = planTempletService.queryPlanByRoot(rootId);
        if (ArrayUtils.isEmpty(allVOs)) {
            return allVOs;
        }
        setDependPlanName(allVOs);

        transAndFillViewVO(allVOs);

        userInfoQuery.tranUserInfoField(allVOs, new String[]{PlanTempletVO.MANAGER}, new String[]{PlanTempletVO.MANAGER_NAME});
        Map<String, List<PlanTempletVO>> childMap = genChildMap(allVOs);
        List<PlanTempletVO> result = childMap.get(StringUtils.EMPTY);
        if (result == null) {
            return new PlanTempletVO[0];
        } else {
            return result.toArray(new PlanTempletVO[0]);
        }
    }

    private Map<String, List<PlanTempletVO>> genChildMap(PlanTempletVO[] allVOs) {
        // 构造树
        Map<String, List<PlanTempletVO>> childMap = new HashMap<>();
        for (PlanTempletVO vo : allVOs) {
            vo.setKey(vo.getPkPlanTemplet());
            String parent = vo.getPkParent();
            if (StringUtils.isEmpty(parent)) {
                parent = StringUtils.EMPTY;
            }
            List<PlanTempletVO> children = childMap.get(parent);
            if (children == null) {
                children = new ArrayList<>();
                childMap.put(parent, children);
            }
            children.add(vo);
        }
        for (PlanTempletVO vo : allVOs) {
            List<PlanTempletVO> children = childMap.get(vo.getPkPlanTemplet());
            if (children != null) {
                vo.setChildren(children.toArray(new PlanTempletVO[0]));
            }
        }
        return childMap;
    }

    private void setDependPlanName(PlanTempletVO[] allVOs){
        Map<String, PlanTempletVO> templetVOMap = new HashMap<String, PlanTempletVO>();
        for(PlanTempletVO vo : allVOs){
            templetVOMap.put(vo.getPkPlanTemplet(), vo);
        }
        if(templetVOMap != null){
            for(PlanTempletVO vo : allVOs){
                if(StringUtils.isNotBlank(vo.getDependTemplet())){
                    PlanTempletVO tempVO = templetVOMap.get(vo.getDependTemplet());
                    if(tempVO != null){
                        vo.setDependPlanName(tempVO.getScode());
                    }
                }
            }
        }
    }

    @PostMapping("/delete")
    public int deletePlanTemp(@RequestBody PlanTempletVO temp, BindingResult errors) throws BusinessException {
        if (StringUtils.isEmpty(temp.getPkPlanTemplet())) {
            errors.rejectValue(PlanTempletVO.PK_PLAN_TEMPLET, null);
        }
        if (StringUtils.isEmpty(temp.getChangeFlag())) {
            errors.rejectValue(CommonFieldConst.CHANGE_FLAG, null);
        }
        if (errors.hasErrors()) {
            throw new IllegalRequestArgException();
        }
        planTempletService.deletePlanTemp(temp.getPkPlanTemplet(), temp.getChangeFlag());
        return 1;
    }

    @RequestMapping("/importPlanFromExcel")
    @ResponseBody
    public PlanTempletVO importPlanFromExcel(@RequestParam(value="file") MultipartFile file,
                                             PlanTempletVO planRoot) throws BusinessException {

        if(planRoot == null) throw new BusinessException("计划模板不能为空！");
        if(file == null) throw new BusinessException("导入文件为空！");
        String fileOriName = file.getOriginalFilename();
        if(!StringUtils.isEmpty(fileOriName)){
            if(!fileOriName.endsWith(".xls") && !fileOriName.endsWith(".xlsx")){
                throw new BusinessException("导入文件不是Excel格式！");
            }
        }else{
            throw new BusinessException("文件信息不全！");
        }

        List<PlanTempletVO> planList = analyzeExcel(file); //解析Excel
        if(planList == null || planList.size() == 0) return planRoot;

        planRoot.setScode("0");  //为了方便处理，根节点编码设为0
        PlanTempletVO[] planVOS = new PlanTempletVO[planList.size() + 1];
        planVOS[0] = planRoot;

        int index = 0;
        for(PlanTempletVO importVO : planList){
            planVOS[++index] = new PlanTempletVO();
            if(importVO == null) continue;
            planVOS[index].setSname(importVO.getSname());
            planVOS[index].setScode(importVO.getScode());
            planVOS[index].setDependPlanName(importVO.getDependPlanName());
            planVOS[index].setExpectDuration(importVO.getExpectDuration());
            planVOS[index].setMilestoneCode(importVO.getMilestoneCode());
            //planVOS[index].setManagerName(importVO.getManagerName());
        }
        planInsertFromExcelCheck(planVOS);  //校验excel导入数据
        // 翻译里程碑编码
        translateMilestoneCode(planVOS);
        buildPlanTree(planVOS, planRoot); //构建树并设置主键等信息
        return planRoot;
    }

    /**
     * 翻译里程碑编码，得到主键和名字
     *
     * @param planVOS
     */
    private void translateMilestoneCode(PlanTempletVO[] planVOS) throws BusinessException {
        Set<String> milestoneCodeSet = new HashSet<>();
        for (PlanTempletVO vo : planVOS) {
            if (StringUtils.isEmpty(vo.getMilestoneCode()))
                continue;

            milestoneCodeSet.add(vo.getMilestoneCode());
        }

        // 查询里程碑档案
        Map<String, MilestoneVO> milestoneVOMap = milestoneQuery.queryByCodes(milestoneCodeSet.toArray(new String[0]),
                RuntimeEnvironment.getTenantId());

        for (PlanTempletVO templetVO : planVOS) {
            if (StringUtils.isEmpty(templetVO.getMilestoneCode()))
                continue;

            MilestoneVO milestoneVO = milestoneVOMap.get(templetVO.getMilestoneCode());
            if (milestoneVO == null)
                continue;

            templetVO.setPkMilestone(milestoneVO.getPkMilestone());
            templetVO.setMilestoneName(milestoneVO.getName());
        }
    }

    /**
     * 解析Excel
     */
    private List<PlanTempletVO> analyzeExcel(MultipartFile file) throws BusinessException {
        //导入Excel
        ImportParams params = new ImportParams();
        params.setTitleRows(0);
        params.setHeadRows(1);
        params.setNeedVerfiy(true);
        ExcelImportResult<PlanTempletVO> result;
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            result = ExcelImportUtil.importExcelMore(inputStream, PlanTempletVO.class, params);
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        } finally {
            if(inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    //ignore
                }
            }
        }
        if(result == null) return new ArrayList<>();
        if(!result.getFailList().isEmpty()){
            throw new BusinessException("请按限定格式导入。");
        }
        return result.getList();
    }
    /**
     * 校验Excel导入的计划节点，计划名称，计划开始、完成日期是否为空，层次是否超过20层
     */
    private void planInsertFromExcelCheck(PlanTempletVO[] planVOS) throws BusinessException{
        if(planVOS == null || planVOS.length < 1) return;
        for(int i = 1; i < planVOS.length; i++){
            if(planVOS[i] != null){
                if(StringUtils.isEmpty(planVOS[i].getSname())){
                    throw new BusinessException("计划名称不能为空！");
                }
                String code = planVOS[i].getScode();
                int level = 1, MAX_LEVEL = 20;
                for (int j = 0; j < code.length(); j++) {
                    if (code.charAt(j) == '.') {
                        level++;
                        if (level > MAX_LEVEL) {
                            throw new BusinessException("计划超过最大层级限制！");
                        }
                    }
                }

                // 工期不能大于等于1，如果为空赋值1
                if (planVOS[i].getExpectDuration() == null) {
                    planVOS[i].setExpectDuration(1);
                } else {
                    if (planVOS[i].getExpectDuration() < 1)
                        throw new BusinessException("存在工期" + planVOS[i].getExpectDuration() + ", 工期不能小于1");
                }
            }
        }
    }
    /**
     * 根据计划编码构造树形关系，并设置前置计划等信息, 输入数据必须要保证父亲节点在前。
     */
    private void buildPlanTree(PlanTempletVO[] planVOS, PlanTempletVO root) {

        HashMap<String, String> recordOldCode = new HashMap<>();  //根据旧编码找新编码

        HashMap<String, ArrayList<PlanTempletVO>> recordChild = new HashMap<>(); //记录所有节点的孩子
        recordChild.put(root.getScode(), new ArrayList<PlanTempletVO>()); //放入根节点
        if(root.getChildCount() == null) root.setChildCount(0);

        for(int i = 1; i < planVOS.length; i++){ //跳过根节点
            PlanTempletVO planVO = planVOS[i];
            if(planVO == null || planVO.getScode() == null) continue;
            String[] codes = planVO.getScode().split("\\.");

            if(codes.length == 1){  //一级节点
                recordChild.get(root.getScode()).add(planVO);
                //重新设置当前节点的编码
                String newCode = (recordChild.get(root.getScode()).size() + root.getChildCount()) + "";
                recordOldCode.put(planVO.getScode(), newCode);
                planVO.setScode(newCode);
            }else if(codes.length  > 1){
                int endIndex = planVO.getScode().length() - codes[codes.length - 1].length() - 1;
                //截取出最长前缀作为父节点编码
                String oldParentCode = planVO.getScode().substring(0, endIndex);
                //根据父节点旧编码查找新编码
                String newParentCode = recordOldCode.get(oldParentCode);
                if(StringUtils.isEmpty(newParentCode)) continue;
                //父节点的孩子加入当前VO
                if(recordChild.get(newParentCode) == null){
                    recordChild.put(newParentCode, new ArrayList<PlanTempletVO>());
                }
                recordChild.get(newParentCode).add(planVO);
                //重新设置当前节点的编码
                String newCode = newParentCode + "." + recordChild.get(newParentCode).size();
                recordOldCode.put(planVO.getScode(), newCode);
                planVO.setScode(newCode);
            }
        }
        for(PlanTempletVO tempVO : planVOS){
            if(tempVO.getDependPlanName() != null){  //前置计划编码修改为新的编码
                tempVO.setDependPlanName(recordOldCode.get(tempVO.getDependPlanName()));
            }
            if(recordChild.get(tempVO.getScode()) != null){
                tempVO.setChildCount(recordChild.get(tempVO.getScode()).size());
                tempVO.setChildren(recordChild.get(tempVO.getScode()).toArray(new PlanTempletVO[0]));
            }
        }
    }

    @PostMapping(value = "/importProject")
    @ResponseBody
    public PlanTempletVO importProject(PlanTempletVO planRoot, @RequestParam(value="file") MultipartFile file) throws BusinessException {
        if(planRoot == null) throw new BusinessException("计划模板不能为空！");
        if(file == null) throw new BusinessException("导入文件为空！");
        //校验文件后缀
        String fileName = file.getOriginalFilename();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        if(!suffix.equals("mpp")) throw new BusinessException("文件格式错误");

        InputStream in = null;
        try{
            in = file.getInputStream();
            MPPReader mppRead = new MPPReader();
            ProjectFile pf = mppRead.read(in);
            List<Task> tasks = pf.getChildTasks();
            Map<String, String> planMap = new HashMap<>();
            getChildrenTask(tasks.get(0), planRoot, planMap);
            genPlan(planRoot, planMap);
        } catch (MPXJException e) {
            throw new BusinessException("文件解析失败");
        } catch (Exception e) {
            throw new BusinessException("导入失败");
        } finally {
            if(in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return planRoot;
    }

    public void getChildrenTask(Task task, PlanTempletVO planVO, Map<String, String> planMap) throws BusinessException {
        if(task.getResourceAssignments().size() == 0){
            List<Task> tasks = task.getChildTasks();
            for (int i = 0; i < tasks.size(); i++) {
                if(tasks.get(i).getResourceAssignments().size() == 0){//说明还是在父任务层
                    PlanTempletVO pro = new PlanTempletVO();
                    //构建子计划
                    buildPlan(planVO, pro, tasks.get(i), planMap);
                    getChildrenTask(tasks.get(i), pro, planMap);
                }else{
                    getChildrenTask(tasks.get(i), planVO, planMap);
                }
            }
        }else{
            getResourceAssignment(task, planVO, planMap);
        }
    }

    public void getResourceAssignment(Task task, PlanTempletVO tempVO, Map<String, String> planMap) throws BusinessException {
        List<ResourceAssignment> list = task.getResourceAssignments();
        if(!list.isEmpty()){
            ResourceAssignment rs = list.get(0);
            //末级开始完成时间从ResourceAssignment中读取
            PlanTempletVO pro = new PlanTempletVO();
            pro.setChildCount(0);
            //构建计划
            buildPlan(tempVO, pro, task, planMap);
            //校验末级节点层级
            planLevelCheck(pro);
        }
    }

    private void planLevelCheck(PlanTempletVO plan) throws BusinessException {
        String code = plan.getScode();
        int level = 1, maxLevel = 20;
        for (int i = 0; i < code.length(); i++) {
            if (code.charAt(i) == '.') {
                level++;
            }
        }
        if (level > maxLevel) {
            throw new BusinessException("计划超过最大层级限制！");
        }
    }

    private void buildPlan(PlanTempletVO parent, PlanTempletVO child, Task task, Map<String, String> planMap) throws BusinessException {
        if(StringUtils.isBlank(task.getName())) throw new BusinessException("计划模板名称不能为空！");
        //设置上级计划childCount
        int childCount = parent.getChildCount() == null ? 0 : parent.getChildCount();
        parent.setChildCount(childCount + 1);
        //构建子计划
        child.setSname(task.getName());
        child.setExpectDuration((int) task.getDuration().getDuration());
        if(StringUtils.isBlank(parent.getScode()) || parent.getScode().equals("0")){
            child.setScode(parent.getChildCount().toString());
        }else{
            child.setScode(parent.getScode()+"."+parent.getChildCount());
        }

        if(!task.getPredecessors().isEmpty()){
            Relation relation = task.getPredecessors().get(0);
            Task targetTask = relation.getTargetTask();
            if(targetTask != null){
                child.setDependPlanName(targetTask.getID().toString());
            }
        }
        planMap.put(task.getID().toString(), child.getScode());

        PlanTempletVO[] childs = parent.getChildren();
        if(childs == null) childs = new PlanTempletVO[0];
        parent.setChildren(ArrayUtils.add(childs, child));

    }

    private void genPlan(PlanTempletVO tempVO, Map<String, String> planMap){
        if(StringUtils.isBlank(tempVO.getPkPlanTemplet()) && StringUtils.isNotBlank(tempVO.getDependPlanName())){
            tempVO.setDependPlanName(planMap.get(tempVO.getDependPlanName()));
        }
        if(tempVO.getChildren() != null){
            for(PlanTempletVO vo : tempVO.getChildren()){
                genPlan(vo, planMap);
            }
        }
    }

    /**
     * 翻译单据类型、部门
     *
     * @param allVOs
     */
    private void transAndFillViewVO(PlanTempletVO[] allVOs) throws BusinessException {
        Set<String> billTypeCodeSet = new HashSet<>();
        Set<String> pkDeptSet = new HashSet<>();
        for (PlanTempletVO plan : allVOs) {
            if (plan.getChildCount() == 0) {
                if (StringUtils.isNotEmpty(plan.getRelatedBillType()))
                    billTypeCodeSet.add(plan.getRelatedBillType());
            }

            if (StringUtils.isNotEmpty(plan.getPkDepartment()))
                pkDeptSet.add(plan.getPkDepartment());
        }

        // 查询单据类型名称
        Map<String, String> billTypeCodeToName = null;
        if (billTypeCodeSet.size() > 0) {
            billTypeCodeToName = queryBillTypeName(billTypeCodeSet.toArray(new String[0]));
        }

        // 查询部门名称
        Map<String, String> pkDeptToName = null;
        if (pkDeptSet.size() > 0) {
            pkDeptToName = queryDepartmentName(pkDeptSet.toArray(new String[0]));
        }

        for (PlanTempletVO vo : allVOs) {
            if (billTypeCodeToName != null && StringUtils.isNotEmpty(vo.getRelatedBillType())) {
                vo.setRelatedBillTypeName(billTypeCodeToName.get(vo.getRelatedBillType()));
            }

            if (pkDeptToName != null && StringUtils.isNotEmpty(vo.getPkDepartment())) {
                vo.setDepartmentName(vo.getPkDepartment());
            }
        }
    }

    /**
     * 查询单据类型名称
     *
     * @param billTypeCodes
     * @return
     */
    private Map<String, String> queryBillTypeName(String[] billTypeCodes) throws BusinessException {
        // 是否查询NC单据类型
        boolean isQueryNcBillType = false;
        for (String billType : billTypeCodes) {
            if (!BillTypeConst.WORKSTART_BILLTYPE.equals(billType) && !BillTypeConst.COMPREHENSIVE_ACCEPTANCE_BILLTYPE.equals(billType)) {
                isQueryNcBillType = true;
                break;
            }
        }

        List<BillTypeRefVO> billTypeRefVOs = new ArrayList<>();
        billTypeRefVOs.add(new BillTypeRefVO(BillTypeConst.WORKSTART_BILLTYPE, BillTypeConst.WORKSTART_BILLTYPE_NAME));
        billTypeRefVOs.add(new BillTypeRefVO(BillTypeConst.COMPREHENSIVE_ACCEPTANCE_BILLTYPE,
                BillTypeConst.COMPREHENSIVE_ACCEPTANCE_BILLTYPE_NAME));
        if (isQueryNcBillType) {
            // 查询nc单据类型
            BillTypeRefVO[] ncBillTypeRefVO = ncBillTypeCustomerQuery.queryRelatedBillTypeForPlan();
            if (ArrayUtils.isNotEmpty(ncBillTypeRefVO)) {
                billTypeRefVOs.addAll(Arrays.asList(ncBillTypeRefVO));
            }
        }

        Map<String, String> result = new HashMap<>();
        Map<String, String> codeToName = new HashMap<>();
        for (BillTypeRefVO refVO : billTypeRefVOs) {
            codeToName.put(refVO.getCode(), refVO.getName());
        }

        for (String code : billTypeCodes) {
            result.put(code, codeToName.get(code));
        }

        return result;
    }

    /**
     * 查询部门名称
     *
     * @param pkDepartments
     * @return
     */
    private Map<String, String> queryDepartmentName(String[] pkDepartments) throws BusinessException {
        Map<String, String> result = new HashMap<>();
        List<NcDepartmentVO> ncDepartmentVOs = ncDeptCustomerQuery
                .queryDepartmentFromNc(new NcDeptQueryParamVO(null, null, Arrays.asList(pkDepartments)));

        if (CollectionUtils.isEmpty(ncDepartmentVOs))
            return result;

        for (NcDepartmentVO vo : ncDepartmentVOs) {
            result.put(vo.getPkDept(), vo.getName());
        }

        return result;
    }

}
